package com.zm.core;

import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.Session;
import com.zm.core.bean.Ssh2Connection;
import com.zm.core.config.GetParamsByProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Vector;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created by ZM on 2022/2/17 14:02
 */
//连接池具体实现类
public class Ssh2ConnectionPools{
    private Logger logger= LoggerFactory.getLogger(Ssh2ConnectionPools.class);
    private String ip;
    private String name;
    private String password;

    private Integer initPoolSize=Integer.valueOf(GetParamsByProperties.readProperties("ssh2.initPoolSize"));
    private Integer incrementSize=Integer.valueOf(GetParamsByProperties.readProperties("ssh2.incrementSize"));
    private Integer maxConnections=Integer.valueOf(GetParamsByProperties.readProperties("ssh2.maxConnections"));
    private Integer maxWaitTimeByOneConnection=Integer.valueOf(GetParamsByProperties.readProperties("ssh2.maxWaitTimeByOneConnection"));
    private Integer idleRatio=Integer.valueOf(GetParamsByProperties.readProperties("ssh2.idleRatio"));
    private Vector connections=null;//存放连接池中ssh2连接的向量，初始为null
    private volatile static AtomicInteger freeNums;//计算空闲连接的个数


    public Ssh2ConnectionPools(String ip,String name,String password){
            this.ip=ip;
            this.name=name;
            this.password=password;
    }
//成员变量getter和setter
    //获取初始连接池大小
    public int getInitPoolSize(){
        return this.initPoolSize;
    }
    //设置初始连接池大小
    public void setInitPoolSize(int initPoolSize){
        this.initPoolSize=initPoolSize;
    }

    public int getIncrementSize() {
        return incrementSize;
    }

    public void setIncrementSize(int incrementSize) {
        this.incrementSize = incrementSize;
    }

    public int getMaxConnections() {
        return maxConnections;
    }

    public void setMaxConnections(int maxConnections) {
        this.maxConnections = maxConnections;
    }

    //创建连接池
    public synchronized void createPool(){
        if (connections!=null){//如果连接池不为空则返回
            return;
        }
        //创建保存连接的向量，初始值为0个元素
        connections=new Vector();
        freeNums=new AtomicInteger(0);
        //根据连接池初始值大小创建连接
        createConnections(this.initPoolSize);
        logger.info("SSH2连接池初始化成功！");
    }

    private void createConnections(int initPoolSize){
        //循环创建指定数目的Ssh2连接
        for (int n=1;n<=initPoolSize;n++){
            if (this.maxConnections>0&&this.connections.size()>=this.maxConnections){
                //若当前创建的连接已经超过最大连接池初始数时
                return;
            }
            try {
                //新增一个连接到连接池向量中
                connections.addElement(new Ssh2Connection(newConnection()));
            } catch (IOException e) {
                logger.error("新建一个SSH服务器连接失败{}"+e.getMessage());
            }
        }
        if (this.connections==null){
            logger.info("SSH服务器连接池创建失败！");
        }else if (this.connections.size()<10){
            logger.info("SSH服务器连接池创建出错，已创建："+this.connections.size()+"个连接");
        }else {
            logger.info("SSH服务器连接池创建新连接成功！");
        }

    }

    //创建一个新的连接
    private Connection newConnection() throws IOException {
        Connection connection=null;
        connection = new Connection(this.ip);
        connection.connect();
        if (!connection.authenticateWithPassword(this.name,this.password)){
            throw new RuntimeException("新建一个SSH服务器连接失败！");
        }
        freeNums.incrementAndGet();//空闲连接数加1
        logger.info("已创建一个新的SSH连接,空闲连接数为："+freeNums.intValue());
        return connection;
    }

    //获得一个可用的连接
    public synchronized Connection getConnection() throws InterruptedException {
        //确保连接池已被创建
        if (connections==null){
            return null;
        }
        Connection conn=getFreeConnection();//获得一个可用的Ssh连接
        long start = System.currentTimeMillis();
        //假设目前没有可用的连接，则等待
        while (conn==null){
            logger.info("当前暂无可用SSH连接，请等待..");
            wait(250);//等待250毫秒
            conn=getFreeConnection();//重新获取一次
            if (System.currentTimeMillis()-start> maxWaitTimeByOneConnection){
                //如果获取一个可用连接等待超过5秒就打破返回null
                break;
            }
        }
        return conn;
    }

    //获取可用连接
    private Connection getFreeConnection() {
        //从连接池中获取一个可用连接
        Connection conn=findFreeConnection();
        if (conn==null){
            //如果当前连接池中没有可用的连接，则尝试新增一些连接
            createConnections(this.incrementSize);
            //重新检查是否有可用连接
            conn=findFreeConnection();
            if (conn==null){
                //如果依然没有可用连接,则返回null
                return null;
            }
        }
        return conn;
    }

    //检查是否有可用连接
    private Connection findFreeConnection() {
        Connection conn=null;
        Ssh2Connection ssh2Connection=null;
        //获取连接池向量中所有的对象
        Enumeration elements = connections.elements();
        //遍历所有对象，看看是否有可用对象
        while (elements.hasMoreElements()){
            ssh2Connection =(Ssh2Connection) elements.nextElement();
            if (!ssh2Connection.getIsConnection()){//如果有对象未被连接，则进行连接，并修改为已连接的状态
                conn=ssh2Connection.getConnection();
                ssh2Connection.setIsConnection(true);
                freeNums.decrementAndGet();//空闲连接数减一
                //测试该连接是否可用
                if (!tryConnection(conn)){
                    //如果该连接已不可用，则创建新的连接，替换旧的不可用的连接
                    try {
                        conn=newConnection();
                    } catch (IOException e) {
                        logger.error("在新建一个SSH服务器连接替换旧连接时，失败！");
                        return null;
                    }
                    ssh2Connection.setConnection(conn);
                }
                //测试可用，退出循环并返回该连接
                break;
            }
        }
        return conn;//返回可用连接
    }

    //测试当前连接是否可用
    private boolean tryConnection(Connection conn) {
            if (conn.isAuthenticationComplete()){
                // 如果至少有一个失败的身份验证请求并且最后一个失败的身份验证请求被服务器标记为“部分成功”，则返回 true。
                return true;
            }
            logger.info("当前SSH连接测试时不可用！");
            closeConnection(conn);//关闭连接并返回false
            return false;
    }

    //释放连接到连接池中
    public void releaseConnection(Connection conn){
            //先判断连接池是否存在
        if (this.connections==null){
            logger.error("SSH连接池不存在，无法将该连接释放回连接池！");
            return;
        }else if (conn==null){
            logger.error("待释放的SSH连接不为空！");
            return;
        }
        Ssh2Connection ssh2Connection=null;
        Enumeration elements = this.connections.elements();
        while (elements.hasMoreElements()){
            ssh2Connection=(Ssh2Connection) elements.nextElement();
            //现在连接池中找到要释放的连接对象
            if (conn==ssh2Connection.getConnection()){
                ssh2Connection.setIsConnection(false);
                //空闲连接数加1操作
                logger.info("已成功释放一个连接到SSH连接池中！当前连接池大小："+this.connections.size()+",空闲连接数："+freeNums.incrementAndGet());
                break;
            }
        }
        //narrowPools();
    }
    //刷新连接池
    public synchronized void refreshPool() throws InterruptedException, IOException {
        //确保连接池已存在
        if (this.connections==null){
            logger.error("SSH连接池不存在，无法刷新！");
            return;
        }
        Ssh2Connection ssh2Connection=null;
        Enumeration elements = this.connections.elements();
        while (elements.hasMoreElements()){//循环遍历所以连接对象
            ssh2Connection=(Ssh2Connection) elements.nextElement();
            if (ssh2Connection.getIsConnection()){//如果该连接忙，则等待一定时间
                wait(3000);//等待3秒
                //关闭此连接，用一个新的连接替换
                closeConnection(ssh2Connection.getConnection());
                ssh2Connection.setConnection(newConnection());
                ssh2Connection.setIsConnection(false);
                logger.info("已刷新替换一个忙碌的SSH连接");
            }
        }

    }
    //关闭连接
    private void closeConnection(Connection conn) {
        conn.close();
        freeNums.decrementAndGet();//连接数减一
        logger.info("已关闭并移除一个SSH连接");
    }

    //缩小连接池
    public synchronized void narrowPools(){//利用定时器定时执行，不再进行每当有连接释放成功后进行一次连接池的缩小
        //规则：空闲连接数占比连接池大小大于等于默认百分之40时，就符合缩小的规则，一直缩小到 占比小于等于4成 或者 连接池大小为初始连接池大小 为止
        if (this.connections==null){
            throw new RuntimeException("SSH连接池不存在，无法进行缩小！");
        }
        if (idleRatio>100||idleRatio<0){
            idleRatio=40;
        }
        if (this.connections.size()<=10||((freeNums.get()*100)/this.connections.size())<idleRatio){
            logger.info("SSH连接池容量正常，无需进行缩小...连接池："+this.connections.size()+"，空闲："+freeNums.get()+"占比不超过百分之"+idleRatio);
            return;
        }
        Ssh2Connection ssh2Connection=null;
        Enumeration elements = this.connections.elements();
        while (elements.hasMoreElements()){
            ssh2Connection=(Ssh2Connection) elements.nextElement();
            if (!ssh2Connection.getIsConnection()&&this.connections.size()>10&&((freeNums.get()*100)/this.connections.size())>=idleRatio){
                closeConnection(ssh2Connection.getConnection());
                this.connections.removeElement(ssh2Connection);
            }
        }
        logger.info("缩小SSH连接池成功！当前连接池大小："+this.connections.size()+",空闲连接数："+freeNums.intValue());
    }

    //关闭连接池
    public synchronized void closePools() throws InterruptedException {
        //确保连接池存在
        if (this.connections==null){
            throw new RuntimeException("SSH连接池不存在，无法关闭！");
        }
        Ssh2Connection ssh2Connection=null;
        Enumeration elements = this.connections.elements();
        while (elements.hasMoreElements()){//循环遍历所以连接对象
            ssh2Connection=(Ssh2Connection) elements.nextElement();
            //如果该连接对象忙，则等待一定时间再删除
            if (ssh2Connection.getIsConnection()){
                //wait(5000);
            }
            closeConnection(ssh2Connection.getConnection());
            this.connections.removeElement(ssh2Connection);
            logger.info("正在关闭SSH连接:"+ssh2Connection);
        }
        this.connections=null;//连接池置为空
        logger.info("SSH连接池清空完毕！");
    }

}
